package demoMod.invincibleOne;

import basemod.*;
import basemod.abstracts.CustomCard;
import basemod.eventUtil.AddEventParams;
import basemod.eventUtil.EventUtils;
import basemod.interfaces.*;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.evacipated.cardcrawl.mod.stslib.Keyword;
import com.evacipated.cardcrawl.modthespire.lib.SpireConfig;
import com.evacipated.cardcrawl.modthespire.lib.SpireInitializer;
import com.google.gson.Gson;
import com.megacrit.cardcrawl.actions.AbstractGameAction;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.core.Settings;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.dungeons.Exordium;
import com.megacrit.cardcrawl.dungeons.TheCity;
import com.megacrit.cardcrawl.helpers.FontHelper;
import com.megacrit.cardcrawl.localization.*;
import com.megacrit.cardcrawl.unlock.UnlockTracker;
import com.megacrit.cardcrawl.vfx.AbstractGameEffect;
import com.megacrit.cardcrawl.vfx.ObtainKeyEffect;
import demoMod.invincibleOne.cards.invincible.*;
import demoMod.invincibleOne.characters.InvincibleOne;
import demoMod.invincibleOne.dynamicVariables.AnotherMagicNumber;
import demoMod.invincibleOne.enums.AbstractCardEnum;
import demoMod.invincibleOne.enums.AbstractPlayerEnum;
import demoMod.invincibleOne.events.Chat;
import demoMod.invincibleOne.events.QuestingAdventurer;
import demoMod.invincibleOne.patches.CharacterSelectPatch;
import demoMod.invincibleOne.relics.*;
import demoMod.invincibleOne.skins.*;
import demoMod.invincibleOne.ui.BingoItem;
import demoMod.invincibleOne.ui.BingoPanel;
import demoMod.invincibleOne.ui.BingoRewardList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import static demoMod.invincibleOne.skins.Skin.skinNow;

@SpireInitializer
public class InvincibleOneMod implements EditCharactersSubscriber,
        EditStringsSubscriber,
        EditCardsSubscriber,
        EditRelicsSubscriber,
        EditKeywordsSubscriber,
        PostInitializeSubscriber,
        PostDungeonInitializeSubscriber,
        PostUpdateSubscriber,
        PostRenderSubscriber,
        StartGameSubscriber,
        AddAudioSubscriber {

    public static final String ATTACK_CARD = "512/bg_attack.png";
    public static final String SKILL_CARD = "512/bg_skill.png";
    public static final String POWER_CARD = "512/bg_power.png";
    private static final String ENERGY_ORB = "512/card_icebreaker_orb.png";
    private static final String CARD_ENERGY_ORB = "512/card_small_orb.png";
    private static final String ATTACK_CARD_PORTRAIT = "1024/bg_attack.png";
    private static final String SKILL_CARD_PORTRAIT = "1024/bg_skill.png";
    private static final String POWER_CARD_PORTRAIT = "1024/bg_power.png";
    private static final String ENERGY_ORB_PORTRAIT = "1024/card_icebreaker_orb.png";

    public static Color mainInvColor = new Color(0.75686F, 0.9294F, 1.0F, 1.0F);
    private static final List<AbstractGameAction> actionList = new ArrayList<>();
    private static final List<AbstractGameAction> parallelActions = new ArrayList<>();
    private static final List<AbstractGameEffect> vfxList = new ArrayList<>();

    public static int[] legendaryCardChance = {0, 0, 0};
    public static int[] seriousLevel = {0, 0, 0};
    public static boolean[] reskinUnlocked = {false, false, false};
    // 被挑衅
    public static boolean[] provoked = {false, false, false};

    public static SpireConfig save;

    public static BingoPanel bingoPanel;
    public static BingoRewardList bingoRewardList;

    static {
        try {
            save = new SpireConfig("invincible-one", "save");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void initialize() {
        new InvincibleOneMod();
        BaseMod.addColor(AbstractCardEnum.INVINCIBLE,
                mainInvColor, mainInvColor, mainInvColor, mainInvColor, mainInvColor, mainInvColor, mainInvColor,
                getResourcePath(ATTACK_CARD), getResourcePath(SKILL_CARD),
                getResourcePath(POWER_CARD), getResourcePath(ENERGY_ORB),
                getResourcePath(ATTACK_CARD_PORTRAIT), getResourcePath(SKILL_CARD_PORTRAIT),
                getResourcePath(POWER_CARD_PORTRAIT), getResourcePath(ENERGY_ORB_PORTRAIT), getResourcePath(CARD_ENERGY_ORB));
    }

    public InvincibleOneMod() {
        BaseMod.subscribe(this);
    }

    public static String makeID(String name) {
        return "Invincible:" + name;
    }

    public static String getResourcePath(String path) {
        return "InvincibleImages/" + path;
    }

    @Override
    public void receiveEditCards() {
        BaseMod.addDynamicVariable(new AnotherMagicNumber());
        List<CustomCard> cards = new ArrayList<>();
        cards.add(new Anarchy());
        cards.add(new Flawless());
        cards.add(new NoOneLives());
        cards.add(new Brutal());
        cards.add(new OutOfThinAir());
        cards.add(new Superconductor());
        cards.add(new Program());
        cards.add(new Reprogram());
        cards.add(new HyperBeam());
        cards.add(new Rushdown());
        cards.add(new Wallop());
        cards.add(new Tantrum());
        cards.add(new Ragnarok());
        cards.add(new RegenVapor());
        cards.add(new TouchingSpeech());
        cards.add(new StandardArmor());
        cards.add(new BodyStrengthen());
        cards.add(new BrothersFrontline());
        cards.add(new FlySwarm());
        //cards.add(new PraiseModel());
        //cards.add(new MonsterPheromone());
        cards.add(new Countermeasure());
        cards.add(new HighPowerWeapon());
        cards.add(new DirtyCursing());
        cards.add(new PowerArmor());
        cards.add(new EmergencyHammer());
        cards.add(new Lenient());
        //cards.add(new Deeper());
        cards.add(new StandReady());
        //cards.add(new StallingTactics());
        cards.add(new Cheerleaders());
        cards.add(new Armaments());
        cards.add(new BBQParty());
        cards.add(new GasolineTank());
        cards.add(new NotDoneYet());
        cards.add(new FootSlip());
        cards.add(new IndustrialRevolution());
        cards.add(new LogisticalServices());
        cards.add(new SwoopIn());
        cards.add(new SporeSpray());
        cards.add(new HotWeapon());
        cards.add(new LavaGround());
        cards.add(new ExponentialGrowth());
        cards.add(new LeechSeed());
        cards.add(new ApplyEvenly());
        //cards.add(new SafetyFirst());
        cards.add(new MedicalSystems());
        cards.add(new HoldGround());
        cards.add(new BloodReflux());
        cards.add(new NeverGiveUp());
        cards.add(new Conscription());
        cards.add(new EnvironmentalEnergy());
        // cards.add(new SuddenPowerOutage());
        cards.add(new Vault());
        cards.add(new CrowdTactics());
        cards.add(new BattleYoga());
        cards.add(new JumpScare());
        cards.add(new CreamyCurse());
        cards.add(new LatestAchievements());
        cards.add(new Latency());
        cards.add(new BulletproofVest());
        cards.add(new ImmuneDecline());
        // cards.add(new Ignore());
        cards.add(new DifficultyBreathing());
        cards.add(new FireHazard());
        cards.add(new Seek());
        //cards.add(new HotAndDry());
        //cards.add(new PayDay());
        //cards.add(new LimitBreak());
        //cards.add(new Reloaded());
        cards.add(new Ambush());
        cards.add(new GlassCanon());
        cards.add(new MechanicalAscent());
        cards.add(new RedSkullCard());
        cards.add(new GetBurned());
        cards.add(new RoadCones());
        cards.add(new HotHotHot());
        cards.add(new MoreIsBetter());
        cards.add(new BigIsBetter());
        cards.add(new Insomnia());
        cards.add(new Transcribe());
        cards.add(new InvincibleForm());
        cards.add(new FlameBarrier());
        cards.add(new Unprepared());
        cards.add(new Hologram());
        cards.add(new Catalyst());
        cards.add(new BronzeScalesCard());
        cards.add(new UserProtocol());
        cards.add(new UnmatchedPower());
        cards.add(new BrokenWatch());
        cards.add(new Aggression());
        cards.add(new SafetyFirst());
        cards.add(new Indignation());
        cards.add(new HeavyAndHeavy());
        for (CustomCard card : cards) {
            BaseMod.addCard(card);
            UnlockTracker.unlockCard(card.cardID);
        }
    }

    private void registerEvents() {
        BaseMod.addEvent((new AddEventParams.Builder(Chat.ID, Chat.class))
                .dungeonID(TheCity.ID)
                .eventType(EventUtils.EventType.NORMAL)
                .endsWithRewardsUI(false).
                spawnCondition(() -> AbstractDungeon.player instanceof InvincibleOne && reskinUnlocked[CardCrawlGame.saveSlot])
                .create());
        BaseMod.addEvent((new AddEventParams.Builder(QuestingAdventurer.ID, QuestingAdventurer.class))
                .dungeonID(Exordium.ID)
                .eventType(EventUtils.EventType.NORMAL)
                .endsWithRewardsUI(false).
                spawnCondition(() -> AbstractDungeon.player instanceof InvincibleOne && AbstractDungeon.player.gold >= 50)
                .create());
    }

    @Override
    public void receiveEditCharacters() {
        BaseMod.addCharacter(new InvincibleOne("InvincibleOne", AbstractPlayerEnum.INVINCIBLE), getResourcePath("charSelect/button.png"), getResourcePath("charSelect/portrait1.png"), AbstractPlayerEnum.INVINCIBLE);
    }

    public static String getLanguageString() {
        String language;
        switch (Settings.language) {
            case ZHS:
                language = "zhs";
                break;
                /*
            case KOR:
                language = "kor";
                break;
            case JPN:
                language = "jpn";
                break;
                */
            default:
                language = "eng";
        }
        return language;
    }

    @Override
    public void receiveEditKeywords() {
        final Gson gson = new Gson();
        String language;
        language = getLanguageString();
        final String json = Gdx.files.internal("localizations/" + language + "/Invincible-KeywordStrings.json").readString(String.valueOf(StandardCharsets.UTF_8));
        com.evacipated.cardcrawl.mod.stslib.Keyword[] keywords = gson.fromJson(json, com.evacipated.cardcrawl.mod.stslib.Keyword[].class);
        if (keywords != null) {
            for (Keyword keyword : keywords) {
                BaseMod.addKeyword("inv", keyword.PROPER_NAME, keyword.NAMES, keyword.DESCRIPTION);
            }
        }
    }

    @Override
    public void receiveEditRelics() {
        BaseMod.addRelicToCustomPool(new TaskGoal(), AbstractCardEnum.INVINCIBLE);
        BaseMod.addRelicToCustomPool(new CostOfBlessing2Strength(), AbstractCardEnum.INVINCIBLE);
        BaseMod.addRelicToCustomPool(new CostOfBlessing2Artifact(), AbstractCardEnum.INVINCIBLE);
        BaseMod.addRelicToCustomPool(new SecondWindRelic(), AbstractCardEnum.INVINCIBLE);
        BaseMod.addRelicToCustomPool(new HeavenScepter(), AbstractCardEnum.INVINCIBLE);
        BaseMod.addRelicToCustomPool(new GoldenLegend(), AbstractCardEnum.INVINCIBLE);
        BaseMod.addRelicToCustomPool(new TheInvinciblesLament(), AbstractCardEnum.INVINCIBLE);
    }

    @Override
    public void receiveEditStrings() {
        String language;
        language = getLanguageString();

        String cardStrings = Gdx.files.internal("localizations/" + language + "/Invincible-CardStrings.json").readString(String.valueOf(StandardCharsets.UTF_8));
        BaseMod.loadCustomStrings(CardStrings.class, cardStrings);
        String relicStrings = Gdx.files.internal("localizations/" + language + "/Invincible-RelicStrings.json").readString(String.valueOf(StandardCharsets.UTF_8));
        BaseMod.loadCustomStrings(RelicStrings.class, relicStrings);
        String powerStrings = Gdx.files.internal("localizations/" + language + "/Invincible-PowerStrings.json").readString(String.valueOf(StandardCharsets.UTF_8));
        BaseMod.loadCustomStrings(PowerStrings.class, powerStrings);
        String charStrings = Gdx.files.internal("localizations/" + language + "/Invincible-CharacterStrings.json").readString(String.valueOf(StandardCharsets.UTF_8));
        BaseMod.loadCustomStrings(CharacterStrings.class, charStrings);
        String uiStrings = Gdx.files.internal("localizations/" + language + "/Invincible-UIStrings.json").readString(String.valueOf(StandardCharsets.UTF_8));
        BaseMod.loadCustomStrings(UIStrings.class, uiStrings);
        String potionStrings = Gdx.files.internal("localizations/" + language + "/Invincible-PotionStrings.json").readString(String.valueOf(StandardCharsets.UTF_8));
        BaseMod.loadCustomStrings(PotionStrings.class, potionStrings);
        String eventStrings = Gdx.files.internal("localizations/" + language + "/Invincible-EventStrings.json").readString(String.valueOf(StandardCharsets.UTF_8));
        BaseMod.loadCustomStrings(EventStrings.class, eventStrings);
    }

    @Override
    public void receivePostDungeonInitialize() {

    }

    public static void saveConfig() {
        save.setInt("legendaryCardChance" + CardCrawlGame.saveSlot, legendaryCardChance[CardCrawlGame.saveSlot]);
        save.setInt("seriousLevel" + CardCrawlGame.saveSlot, seriousLevel[CardCrawlGame.saveSlot]);
        try {
            save.save();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    private static void emptyUpdate(ModLabel b){}
    private static void updatePVPToggle(ModToggleButton b) {
        isOnlinePVP = b.enabled;

        InvincibleOneMod.save.setBool("isOnlinePVP", isOnlinePVP);
        try {
            InvincibleOneMod.save.save();
        } catch (Exception e) {
        }
    }
    public static boolean isOnlinePVP;
    private static ModLabeledToggleButton pvpToggle() {
        return new ModLabeledToggleButton(CardCrawlGame.languagePack.getUIString(InvincibleOneMod.makeID("PVPToggleText")).TEXT[0],
                350.0F, 650F, Color.WHITE,
                FontHelper.tipBodyFont, isOnlinePVP, panel, InvincibleOneMod::emptyUpdate, InvincibleOneMod::updatePVPToggle);
    }
    private static void updateRelicToggle(ModToggleButton b) {
        isRelicValid = b.enabled;

        InvincibleOneMod.save.setBool("isRelicValid", isRelicValid);
        try {
            InvincibleOneMod.save.save();
        } catch (Exception e) {
        }
    }
    public static boolean isRelicValid;
    private static ModLabeledToggleButton relicToggle() {
        return new ModLabeledToggleButton(CardCrawlGame.languagePack.getUIString(InvincibleOneMod.makeID("RelicToggleText")).TEXT[0],
                350.0F, 550F, Color.WHITE,
                FontHelper.tipBodyFont, isRelicValid, panel, InvincibleOneMod::emptyUpdate, InvincibleOneMod::updateRelicToggle);
    }
    public static ModPanel panel;
    // mod设置界面
    public static void buildUI() {
        panel = new ModPanel();
        panel.addUIElement(pvpToggle());
        panel.addUIElement(relicToggle());
    }

    private static final Logger logger = LogManager.getLogger(InvincibleOneMod.class.getName());
    @Override
    public void receivePostInitialize() {
        try {
            save.load();
            TaskGoal.getLastGameHistory();

            for (int i=0; i<3; i++) {
                if (save.has("legendaryCardChance" + i)) {
                    legendaryCardChance[i] = save.getInt("legendaryCardChance" + i);
                }
                if (save.has("seriousLevel" + i)) {
                    seriousLevel[i] = save.getInt("seriousLevel" + i);
                }
                if (save.has("reskinIndex" + i)) {
                    CharacterSelectPatch.skinIndex[i] = 0;
                    String skinID = save.getString("reskinIndex" + i);
                    boolean found = false;
                    for (int j=0; j<Skin.skins.size(); j++) {
                        if (Skin.skins.get(j).skinID.equals(skinID)) {
                            CharacterSelectPatch.skinIndex[i] = j;
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        logger.warn("The skin {} is not found!", skinID);
                    }
                }
                if (save.has("reskinUnlocked" + i)) {
                    reskinUnlocked[i] = save.getBool("reskinUnlocked" + i);
                }
                if (save.has("provoked" + i)) {
                    provoked[i] = save.getBool("provoked" + i);
                }
            }
            if (save.has("isOnlinePVP")) {
                isOnlinePVP = save.getBool("isOnlinePVP");
            }
            if (save.has("isRelicValid")) {
                isRelicValid = save.getBool("isRelicValid");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        registerEvents();
        bingoPanel = new BingoPanel();
        bingoRewardList = new BingoRewardList();
        BingoItem.values();

        buildUI();
        BaseMod.registerModBadge(new Texture(1, 1, Pixmap.Format.RGBA8888), "TheInvincible", "vodkakaa", "TheInvincible", panel);
    }

    @Override
    public void receivePostRender(SpriteBatch sb) {
        vfxList.forEach(e -> e.render(sb));
        if (bingoPanel != null) {
            bingoPanel.render(sb);
        }
        if (bingoRewardList != null) {
            bingoRewardList.render(sb);
        }
        CardCrawlGame.cursor.render(sb);
    }

    public static void addToBot(AbstractGameAction action) {
        actionList.add(action);
    }

    public static void addParallel(AbstractGameAction action) {
        parallelActions.add(action);
    }

    public static void addVfx(AbstractGameEffect effect) {
        vfxList.add(effect);
    }

    @Override
    public void receivePostUpdate() {
        if (!actionList.isEmpty()) {
            actionList.get(0).update();
            if (actionList.get(0).isDone) {
                actionList.remove(0);
            }
        }
        parallelActions.forEach(AbstractGameAction::update);
        parallelActions.removeIf(action -> action.isDone);
        vfxList.forEach(AbstractGameEffect::update);
        vfxList.removeIf(e -> e.isDone);
        if (bingoPanel != null) {
            bingoPanel.update();
        }
        if (bingoRewardList != null) {
            bingoRewardList.update();
        }
    }

    @Override
    public void receiveStartGame() {
        Settings.hideTopBar = false;
        if (AbstractDungeon.player instanceof InvincibleOne) {
            if (!Settings.hasRubyKey) {
                AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.RED));
            }

            if (!Settings.hasSapphireKey) {
                AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.BLUE));
            }

            if (!Settings.hasEmeraldKey) {
                AbstractDungeon.topLevelEffects.add(new ObtainKeyEffect(ObtainKeyEffect.KeyColor.GREEN));
            }
            AbstractDungeon.isAscensionMode = true;
            AbstractDungeon.ascensionLevel = -seriousLevel[CardCrawlGame.saveSlot];
            TaskGoal.getLastGameHistory();
            addToBot(new AbstractGameAction() {
                @Override
                public void update() {
                    if (AbstractDungeon.floorNum == 0) {
                        bingoPanel.initBingoPanel();
                        bingoRewardList.getRewards().clear();
                        if (seriousLevel[CardCrawlGame.saveSlot] >= 10) {
                            new SecondWindRelic().instantObtain();
                        }

                        InvincibleOneMod.provoked[CardCrawlGame.saveSlot] = false;
                        save.setBool("provoked" + CardCrawlGame.saveSlot, false);
                        try {
                            save.save();
                        } catch (IOException e) {}

                        Skin.skinNow().onGameStart();
                    }
                    isDone = true;
                }
            });
        }
    }

    @Override
    public void receiveAddAudio() {
        BaseMod.addAudio("SFX_BINGO_FAILED", "InvincibleAudio/sfx/sfx_bingo_failed.wav");
    }
}
